import 'dart:convert';
import 'dart:typed_data';
import 'package:commons/commons.dart';
import 'package:encrypt/encrypt.dart';

/// AES 加密模式枚举
enum MIUMode {
  ecb(1), // ECB 模式
  cbc(2), // CBC 模式
  cfb(3), // CFB 模式
  ofb(7); // OFB 模式

  const MIUMode(this.value);
  final int value;
}

/// AES 密钥大小枚举
enum MIUKeySizeAES {
  key128(16), // 128位
  key192(24), // 192位
  key256(32); // 256位

  const MIUKeySizeAES(this.value);
  final int value;
}

/// AES 填充模式枚举
enum MIUCryptorPadding {
  noPadding(0), // 无填充
  pkcs7(1), // PKCS7 填充
  zeroPadding(2), // 零填充
  ansix923(3), // ANSI X9.23 填充
  iso10126(4); // ISO 10126 填充

  const MIUCryptorPadding(this.value);
  final int value;
}

/// MIUAES 加密工具类
/// MIUAES 类，提供 AES 加密解密功能
class MIUAES {
  /// AES 加密字符串
  ///
  /// [originalStr] 要加密的原始字符串
  /// [mode] 加密模式
  /// [key] 加密密钥
  /// [keySize] 密钥大小
  /// [iv] 初始化向量（CBC 模式必需）
  /// [padding] 填充模式
  ///
  /// 返回 Base64 编码的加密结果
  static String miuaesEncrypt(
    String originalStr,
    MIUMode mode,
    String key,
    MIUKeySizeAES keySize, {
    String? iv,
    MIUCryptorPadding padding = MIUCryptorPadding.pkcs7,
  }) {
    try {
      final data = utf8.encode(originalStr);
      final result = _miuaesWithData(
        Uint8List.fromList(data),
        true, // 加密
        mode,
        key,
        keySize,
        iv: iv,
        padding: padding,
      );
      return base64.encode(result);
    } catch (e) {
      throw Exception('AES 加密错误: $e');
    }
  }

  /// AES 解密字符串
  ///
  /// [originalStr] 要解密的 Base64 编码字符串
  /// [mode] 解密模式
  /// [key] 解密密钥
  /// [keySize] 密钥大小
  /// [iv] 初始化向量（CBC 模式必需）
  /// [padding] 填充模式
  ///
  /// 返回解密后的原始字符串
  static String miuaesDecrypt(
    String originalStr,
    MIUMode mode,
    String key,
    MIUKeySizeAES keySize, {
    String? iv,
    MIUCryptorPadding padding = MIUCryptorPadding.pkcs7,
  }) {
    try {
      // 预处理：移除可能的转义字符和空白字符
      String cleanData = originalStr.replaceAll('\\/', '/').trim();

      if (cleanData.isEmpty) {
        throw const FormatException('输入数据为空，无法进行Base64解码');
      }

      if (!_isValidBase64(cleanData)) {
        throw FormatException(
            '输入不是有效的 Base64 字符串: ${cleanData.length > 50 ? "${cleanData.substring(0, 50)}..." : cleanData}');
      }

      final data = base64.decode(cleanData);

      // 检查解码后的数据长度
      if (data.isEmpty) {
        throw const FormatException('Base64 解码后数据为空');
      }

      final result = _miuaesWithData(
        Uint8List.fromList(data),
        false, // 解密
        mode,
        key,
        keySize,
        iv: iv,
        padding: padding,
      );

      // 检查解密结果
      if (result.isEmpty) {
        throw const FormatException('解密结果为空');
      }

      final decoded = utf8.decode(result);

      return decoded;
    } catch (e) {
      throw Exception('AES 解密错误: $e');
    }
  }

  /// 核心加密/解密方法
  static Uint8List _miuaesWithData(
    Uint8List originalData,
    bool operation, // true: 加密, false: 解密
    MIUMode mode,
    String key,
    MIUKeySizeAES keySize, {
    String? iv,
    MIUCryptorPadding padding = MIUCryptorPadding.pkcs7,
  }) {
    // 断言检查
    assert(mode != MIUMode.cbc || (iv != null && iv.isNotEmpty),
        '使用 CBC 模式，initializationVector（即iv，填充值）必须有值');

    // 处理密钥和 IV
    final keyData = _processKeyData(key, keySize);
    final ivData = _processIVData(iv);

    // 手动填充
    Uint8List sourceData = originalData;
    if (operation) {
      sourceData = _bitPaddingWithData(sourceData, mode, padding);
    }

    // 使用 encrypt 库进行加密/解密
    Uint8List result;
    if (operation) {
      result =
          _encryptWithEncryptLib(sourceData, keyData, ivData, mode, padding);
    } else {
      result =
          _decryptWithEncryptLib(sourceData, keyData, ivData, mode, padding);
    }

    // 解密时移除填充
    if (!operation) {
      result = _removeBitPaddingWithData(result, mode, padding);
    }

    return result;
  }

  /// 处理密钥数据
  static Uint8List _processKeyData(String key, MIUKeySizeAES keySize) {
    final keyBytes = utf8.encode(key);
    final int targetLength = keySize.value; // 直接使用枚举值

    // 截断到指定长度，不足的用0填充
    final result = Uint8List(targetLength);
    for (int i = 0; i < targetLength; i++) {
      if (i < keyBytes.length) {
        result[i] = keyBytes[i];
      } else {
        result[i] = 0; // 不足的部分用0填充
      }
    }
    return result;
  }

  /// 处理 IV 数据
  static Uint8List _processIVData(String? iv) {
    if (iv == null || iv.isEmpty) {
      return Uint8List(16);
    }

    final ivBytes = utf8.encode(iv);
    final result = Uint8List(16); // AES 块大小固定为 16

    // 截断到16字节，不足的用0填充
    for (int i = 0; i < 16; i++) {
      if (i < ivBytes.length) {
        result[i] = ivBytes[i];
      } else {
        result[i] = 0; // 不足的部分用0填充
      }
    }
    return result;
  }

  /// 手动填充数据
  static Uint8List _bitPaddingWithData(
    Uint8List data,
    MIUMode mode,
    MIUCryptorPadding padding,
  ) {
    const int blockSize = 16; // AES 块大小固定为 16

    switch (padding) {
      case MIUCryptorPadding.pkcs7:
        if (mode == MIUMode.cfb || mode == MIUMode.ofb) {
          // 只在 CFB 和 OFB 模式下进行手动填充
          final int shouldLength =
              blockSize * ((data.length / blockSize).floor() + 1);
          final int diffLength = shouldLength - data.length;

          final result = Uint8List(shouldLength);
          result.setRange(0, data.length, data);

          // 填充
          for (int i = 0; i < diffLength; i++) {
            result[data.length + i] = diffLength;
          }
          return result;
        }
        // 对于其他模式（如 CBC），不进行手动填充
        return data;

      case MIUCryptorPadding.zeroPadding:
        final int diff = blockSize - (data.length % blockSize);
        final result = Uint8List(data.length + diff);
        result.setRange(0, data.length, data);
        // 用0填充
        for (int i = 0; i < diff; i++) {
          result[data.length + i] = 0;
        }
        return result;

      case MIUCryptorPadding.ansix923:
        final int diff = blockSize - (data.length % blockSize);
        final result = Uint8List(data.length + diff);
        result.setRange(0, data.length, data);
        // 用0填充，最后一个字节填充diff值
        for (int i = 0; i < diff - 1; i++) {
          result[data.length + i] = 0;
        }
        result[data.length + diff - 1] = diff;
        return result;

      case MIUCryptorPadding.iso10126:
        final int diff = blockSize - (data.length % blockSize);
        final result = Uint8List(data.length + diff);
        result.setRange(0, data.length, data);
        // 用随机数填充，最后一个字节填充diff值
        for (int i = 0; i < diff - 1; i++) {
          result[data.length + i] =
              (DateTime.now().millisecondsSinceEpoch + i) % 254 + 1;
        }
        result[data.length + diff - 1] = diff;
        return result;

      case MIUCryptorPadding.noPadding:
      default:
        return data;
    }
  }

  /// 移除填充数据
  static Uint8List _removeBitPaddingWithData(
    Uint8List sourceData,
    MIUMode mode,
    MIUCryptorPadding padding,
  ) {
    if (sourceData.isEmpty) {
      return sourceData;
    }

    const int blockSize = 16;
    final int lastByte = sourceData[sourceData.length - 1];
    int correctLength = 0;

    switch (padding) {
      case MIUCryptorPadding.pkcs7:
        if ((mode == MIUMode.cfb || mode == MIUMode.ofb) &&
            (lastByte > 0 && lastByte < blockSize + 1)) {
          correctLength = sourceData.length - lastByte;
        } else {
          // 对于其他模式，encrypt 库会自动处理填充移除
          return sourceData;
        }
        break;

      case MIUCryptorPadding.zeroPadding:
        if (lastByte == 0) {
          for (int i = sourceData.length - 1; i > 0; i--) {
            if (sourceData[i] != lastByte) {
              correctLength = i + 1;
              break;
            }
          }
        }
        break;

      case MIUCryptorPadding.ansix923:
      case MIUCryptorPadding.iso10126:
        if (lastByte > 0 && lastByte < blockSize + 1) {
          correctLength = sourceData.length - lastByte;
        }
        break;

      case MIUCryptorPadding.noPadding:
      default:
        return sourceData;
    }

    if (correctLength > 0 && correctLength <= sourceData.length) {
      return sourceData.sublist(0, correctLength);
    }

    return sourceData;
  }

  /// 使用 encrypt 库进行加密
  static Uint8List _encryptWithEncryptLib(
    Uint8List data,
    Uint8List key,
    Uint8List iv,
    MIUMode mode,
    MIUCryptorPadding padding,
  ) {
    final aesMode = _convertMode(mode);
    final keyObj = Key(key);
    final ivObj = IV(iv);

    // 根据模式和填充类型决定是否使用自动填充
    if (mode == MIUMode.cbc && padding == MIUCryptorPadding.pkcs7) {
      // 对于 CBC + PKCS7，使用 encrypt 库的默认填充（模拟 CommonCrypto 的行为）
      final encrypter = Encrypter(AES(keyObj, mode: aesMode));
      final encrypted = encrypter.encryptBytes(data, iv: ivObj);
      return Uint8List.fromList(encrypted.bytes);
    } else {
      // 对于其他情况，使用手动填充的数据
      final encrypter = Encrypter(AES(keyObj, mode: aesMode, padding: null));
      final encrypted = encrypter.encryptBytes(data, iv: ivObj);
      return Uint8List.fromList(encrypted.bytes);
    }
  }

  /// 使用 encrypt 库进行解密
  static Uint8List _decryptWithEncryptLib(
    Uint8List data,
    Uint8List key,
    Uint8List iv,
    MIUMode mode,
    MIUCryptorPadding padding,
  ) {
    final aesMode = _convertMode(mode);
    final keyObj = Key(key);
    final ivObj = IV(iv);

    // 根据模式和填充类型决定是否使用自动填充
    if (mode == MIUMode.cbc && padding == MIUCryptorPadding.pkcs7) {
      // 对于 CBC + PKCS7，使用 encrypt 库的默认填充
      final encrypter = Encrypter(AES(keyObj, mode: aesMode));
      final encrypted = Encrypted(data);
      final decrypted = encrypter.decryptBytes(encrypted, iv: ivObj);
      return Uint8List.fromList(decrypted);
    } else {
      // 对于其他情况，使用手动填充的数据
      final encrypter = Encrypter(AES(keyObj, mode: aesMode, padding: null));
      final encrypted = Encrypted(data);
      final decrypted = encrypter.decryptBytes(encrypted, iv: ivObj);
      return Uint8List.fromList(decrypted);
    }
  }

  /// 转换模式
  static AESMode _convertMode(MIUMode mode) {
    switch (mode) {
      case MIUMode.ecb:
        return AESMode.ecb;
      case MIUMode.cbc:
        return AESMode.cbc;
      case MIUMode.cfb:
        return AESMode.cfb64;
      case MIUMode.ofb:
        return AESMode.ofb64;
    }
  }

  /// 检查是否为有效的 Base64 字符串
  static bool _isValidBase64(String str) {
    if (str.isEmpty) {
      return false;
    }

    try {
      // 移除可能的空白字符
      final cleanStr = str.replaceAll(RegExp(r'\s+'), '');

      // 再次检查清理后是否为空
      if (cleanStr.isEmpty) {
        return false;
      }

      // 检查长度是否为4的倍数
      if (cleanStr.length % 4 != 0) {
        return false;
      }

      // 尝试解码
      base64.decode(cleanStr);
      return true;
    } catch (e) {
      return false;
    }
  }

  /// 解密数据的静态方法
  ///
  /// [encryptedData] 加密的Base64字符串
  /// [key] AES密钥
  /// [iv] 初始化向量
  ///
  /// 返回解密后的JSON字符串
  static String decryptData(
    String encryptedData,
    String key,
    String iv,
  ) {
    try {
      // 参数验证
      if (encryptedData.isEmpty) {
        throw ArgumentError('加密数据不能为空');
      }
      if (key.isEmpty) {
        throw ArgumentError('密钥不能为空');
      }
      if (iv.isEmpty) {
        throw ArgumentError('初始化向量不能为空');
      }

      // 预处理：移除可能的转义字符和空白字符
      String cleanData = encryptedData.replaceAll('\\/', '/').trim();

      return miuaesDecrypt(
        cleanData,
        MIUMode.cbc,
        key,
        MIUKeySizeAES.key256,
        iv: iv,
        padding: MIUCryptorPadding.pkcs7,
      );
    } catch (e) {
      throw Exception('数据解密错误: $e');
    }
  }

  /// 加密数据（使用特定JSON序列化方式）
  static String encryptCompatible(
    Map<String, dynamic> params,
    String key,
    String iv,
  ) {
    // 特定字段顺序，所有值转为字符串
    final styleJson = _generateStyleJson(params);
    return miuaesEncrypt(
      styleJson,
      MIUMode.cbc,
      key,
      MIUKeySizeAES.key256,
      iv: iv,
      padding: MIUCryptorPadding.pkcs7,
    );
  }

  /// 生成特定风格的JSON字符串
  static String _generateStyleJson(Map<String, dynamic> params) {
    // 特定字段顺序，uid字段不参与顺序排序
    final orderedKeys = ['utoken', 'appid', 'from'];
    final Map<String, dynamic> orderedParams = {};

    // 首先添加已知顺序的字段（除了uid）
    for (String key in orderedKeys) {
      if (params.containsKey(key)) {
        // 所有值都转换为字符串
        orderedParams[key] = params[key].toString();
      }
    }

    // 然后添加uid字段（如果有的话）
    if (params.containsKey('uid')) {
      orderedParams['uid'] = params['uid'].toString();
    }

    // 最后添加其他字段
    for (String key in params.keys) {
      if (!orderedKeys.contains(key) && key != 'uid') {
        orderedParams[key] = params[key].toString();
      }
    }

    return jsonEncode(orderedParams);
  }
}

/// 扩展 MIUAES 类，添加便捷方法
extension MIUAESExtension on String {
  /// 便捷的加密方法，使用默认参数，自动兼容
  String encrypt() {
    final m = Get.userCtrl.state.rsa;
    return MIUAES.miuaesEncrypt(
      this,
      MIUMode.cbc,
      m?.key ?? '',
      MIUKeySizeAES.key256,
      iv: m?.iv ?? '',
      padding: MIUCryptorPadding.pkcs7,
    );
  }

  /// 智能加密方法，如果是JSON字符串会自动转换为特定风格
  String encryptCompatible() {
    final m = Get.userCtrl.state.rsa;
    if (m?.key != null && m?.iv != null) {
      // 尝试解析为JSON，如果成功则使用特定风格加密
      try {
        final jsonData = jsonDecode(this) as Map<String, dynamic>;
        return MIUAES.encryptCompatible(jsonData, m!.key!, m.iv!);
      } catch (e) {
        // 如果不是JSON，则使用普通加密
        return encrypt();
      }
    } else {
      return encrypt();
    }
  }

  /// 安全的解密方法，不会抛出异常
  /// 如果解密失败，返回null而不是抛出异常
  String? safeDecrypt() {
    try {
      return decrypt();
    } catch (e) {
      return null;
    }
  }

  /// 解析加密数据的方法
  String decrypt() {
    try {
      // 预处理：移除可能的转义字符和空白字符
      String cleanData = replaceAll('\\/', '/').trim();

      // 检查输入是否为空
      if (cleanData.isEmpty) {
        return '';
      }

      // 使用默认的RSA配置进行解密
      final m = Get.userCtrl.state.rsa;
      if (m == null) {
        throw Exception('RSA配置未找到');
      }

      if (m.key == null || m.key!.isEmpty) {
        throw Exception('RSA密钥为空');
      }

      if (m.iv == null || m.iv!.isEmpty) {
        throw Exception('RSA初始化向量为空');
      }

      return MIUAES.miuaesDecrypt(
        cleanData,
        MIUMode.cbc,
        m.key!,
        MIUKeySizeAES.key256,
        iv: m.iv!,
        padding: MIUCryptorPadding.pkcs7,
      );
    } catch (e) {
      // 对于解密失败，返回空字符串而不是抛出异常，避免应用崩溃
      return '';
    }
  }
}

extension MIUAESDataExtension on Map<String, dynamic> {
  /// 便捷的加密方法，使用默认参数，自动兼容
  Map<String, dynamic> encrypt() {
    final m = Get.userCtrl.state.rsa;
    if (m?.key != null && m?.iv != null) {
      // 使用兼容的加密方法
      final aseData = MIUAES.encryptCompatible(this, m!.key!, m.iv!);
      return {'encrypt_data': aseData};
    } else {
      // 降级到普通JSON加密
      final jsonString = jsonEncode(this);
      final aseData = jsonString.encrypt();
      return {'encrypt_data': aseData};
    }
  }

  /// 使用特定风格的JSON序列化进行加密
  Map<String, dynamic> encryptStyle() {
    final m = Get.userCtrl.state.rsa;
    if (m?.key != null && m?.iv != null) {
      final aseData = MIUAES.encryptCompatible(this, m!.key!, m.iv!);
      return {'encrypt_data': aseData};
    } else {
      throw Exception('RSA密钥或IV未设置，无法进行特定风格加密');
    }
  }
}
